﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NLite.Mini.Internal;
using System.Reflection;
using NLite.Mini.Attributes;
using NLite.Reflection;
using System.Configuration;
using NLite.Internal;

namespace NLite.Mini.Listener
{
    /// <summary>
    /// 
    /// </summary>
    public class AppSettingInjectionListener:ComponentListenerAdapter
    {
        /// <summary>
        /// 
        /// </summary>
        public AppSettingInjectionListener()
            : base(ComponentListenStage.PostCreation)
        {

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ctx"></param>
        public override void OnPostCreation(NLite.Mini.Context.IPostCreationContext ctx)
        {
            if (ctx.Component.Implementation == null
                || ctx.Instance == null)
                return;

            Lazy<MemberModel[]> lazy = null;
            if (ctx.Component.ExtendedProperties.Contains("AppSettings"))
                lazy = ctx.Component.ExtendedProperties["AppSettings"] as Lazy<MemberModel[]>;

            if (lazy == null)
            {
                const BindingFlags Flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;

                lazy = new Lazy<MemberModel[]>(() =>
                {
                    var instanceType = ctx.Component.Implementation;
                    return (from item in instanceType.GetFields(Flags)
                            let attr = item.GetAttribute<NLite.SettingAttribute>(true)
                            where attr != null &&  !string.IsNullOrEmpty(attr.Name)
                            select new MemberModel { Id = attr.Name, MemberType = item.FieldType, Setter = DynamicMethodFactory.GetSetter(item) })
                                .Union(
                                from item in instanceType.GetProperties(Flags)
                                let attr = item.GetAttribute<NLite.SettingAttribute>(true)
                                let ps = item.GetIndexParameters()
                                where attr != null && !string.IsNullOrEmpty(attr.Name) && (ps == null || ps.Length == 0)
                                select new MemberModel { Id = attr.Name, MemberType = item.PropertyType, Setter = DynamicMethodFactory.GetSetter(item) })
                                 .Union(
                                from item in instanceType.GetMethods(Flags)
                                let attr = item.GetAttribute<NLite.SettingAttribute>(true)
                                let ps = item.GetParameters()
                                where attr != null && !string.IsNullOrEmpty(attr.Name) && (ps == null || ps.Length == 0) && item.ReturnType != Types.Void
                                select new MemberModel { Id = attr.Name, MemberType = item.ReturnType, Setter = DynamicMethodFactory.GetSetter(item) })
                                .ToArray();
                });

                ctx.Component.ExtendedProperties["AppSettings"] = lazy;
            }


            if (lazy.Value.Length == 0)
                return;

            foreach (var item in lazy.Value)
                item.Setter(ctx.Instance, GetValueByAppSettingsOrPropertyManager(item));
        }

        private object GetValueByAppSettingsOrPropertyManager(MemberModel item)
        {
            if (ConfigurationManager.AppSettings.AllKeys.Contains(item.Id))
            {
                return Mapper.Map(ConfigurationManager.AppSettings[item.Id], Types.String, item.MemberType);
            }
            else if (PropertyManager.Instance.Properties.Contains(item.Id))
            {
                var o =  PropertyManager.Instance.Properties[item.Id];
                if (o != null)
                {
                    var sv = o as SerializedValue;
                    if (sv != null)
                        o = sv.Deserialize(item.MemberType);

                    return Mapper.Map(o, o.GetType(), item.MemberType);
                }
            }

            return null;
        }
    }
}
